/**
 * 
 */
package de.dnb.basics.statistics;

import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import de.dnb.basics.applicationComponents.Pair;

/**
 * Klasse, die eine Häufigkeitsverteilung implementiert. Objekte des Typs <V>
 * (values) werden gezählt und können mit Häufigkeit ausgegeben werden.
 * 
 * @param <V>	Typ der Objekte, die gezählt werden.
 * 
 * @author Christian_2
 *
 */
public class Frequency<V> implements Iterable<V> {

    protected final Map<V, Long> values2long = new HashMap<V, Long>();

    /**
     * Fügt einen weiteren Eintrag für value hinzu.
     * 
     * @param value auch null
     */
    public final void add(final V value) {
        Long l = values2long.get(value);
        if (l == null) {
            l = new Long(1);
        } else {
            l++;
        }
        values2long.put(value, l);
    }

    /**
     * 
     * Fügt increment Einträge für value hinzu.
     * 
     * @param value		auch null
     * @param increment	Differenz
     */
    public final void increment(final V value, final long increment) {
        Long l = values2long.get(value);
        if (l == null) {
            l = new Long(increment);
        } else {
            l += increment;
        }
        values2long.put(value, l);
    }

    /**
     * Setzt zurück.
     */
    public final void clear() {
        values2long.clear();
    }

    /**
     * 
     * @return Häufigkeitverteilung als Menge von {@link Entry}.
     */
    public final Set<Map.Entry<V, Long>> getEntries() {
        return values2long.entrySet();
    }

    /**
     * 
     * @return Häufigkeitverteilung als Menge von {@link Pair}.
     */
    public final Collection<Pair<V, Long>> getDistribution() {
        return Pair.getPairs(getEntries());
    }

    /**
     * 
     * @return	Iterator über die gezählten Werte + Anzahl
     */
    public final Iterator<Map.Entry<V, Long>> entrySetIterator() {
        return values2long.entrySet().iterator();
    }

    /**
     * 
     * @return	Iterator über die gezählten Werte
     */
    public final Iterator<V> valuesIterator() {
        return values2long.keySet().iterator();
    }

    /**
     * 
     * @return Summe aller Häufigkeiten
     */
    public final long getSum() {
        Iterator<Map.Entry<V, Long>> it = entrySetIterator();
        long l = 0;
        while (it.hasNext()) {
            Map.Entry<V, Long> entry = it.next();
            l += entry.getValue();
        }
        return l;
    }

    /**
     * 
     * @param value	auch null
     * @return		Anzahl der Einträge von value
     */
    public final long getCount(final V value) {
        Long l = values2long.get(value);
        if (l == null)
            return 0;
        else
            return l;
    }

    /**
     * 
     * @return  Zahl der Einträge
     */
    public final long getSize() {
        return values2long.size();
    }

    public final double getAverage() {
        return (double) getSum() / (double) getSize();
    }

    /**
     * 
     * @param value	auch null
     * @return		value enthalten
     */
    public final boolean contains(final V value) {
        return getCount(value) != 0L;
    }

    @Override
    public String toString() {
        Set<Map.Entry<V, Long>> set = getEntries();
        String s = "";
        for (Iterator<Entry<V, Long>> iterator = set.iterator(); iterator
            .hasNext();) {
            Map.Entry<V, Long> entry = iterator.next();
            s += entry.getKey() + "\t" + entry.getValue();
            if (iterator.hasNext())
                s += "\n";
        }
        return s;
    }

    /**
     * @param args
     */
    public static void main(String[] args) {
        Frequency<String> frequency = new Frequency<>();
        frequency.add("1");
        frequency.add("1");
        frequency.add(null);
        frequency.increment("s", 7);
        System.out.println(frequency);
        System.out.println(frequency.getAverage());
    }

    @Override
    public final Iterator<V> iterator() {
        return valuesIterator();
    }

}
